﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics.Contracts;

namespace SimpleIR
{
	/// <summary>
	/// A representation for interning and tracking all the program variable names and fields.
	/// </summary>
	public sealed class IRIdentifierTable
	{
		//Internal table for variables
		private readonly Dictionary<String, IRVarName> m_varInternTable;
		private readonly Dictionary<Tuple<IRType, string>, IRStaticField> m_staticInternTable;

		public readonly CtrGen ExternalRefCtrGen;

		[ContractInvariantMethod]
		private void ObjectInvariant()
		{
			Contract.Invariant(this.m_varInternTable != null);
			Contract.Invariant(this.m_staticInternTable != null);

			Contract.Invariant(this.ExternalRefCtrGen != null);
		}

		public IRIdentifierTable()
		{
			this.m_varInternTable = new Dictionary<string, IRVarName>();
			this.m_staticInternTable = new Dictionary<Tuple<IRType, string>, IRStaticField>();

			this.ExternalRefCtrGen = new CtrGen();
		}

		////////////////////////////////////////////
		//For local variables

		/// <summary>
		/// Intern a new local variable with the given name.
		/// </summary>
		internal IRVarName InternNewLocalVar(string name)
		{
			Contract.Requires(!String.IsNullOrEmpty(name));
			Contract.Ensures(Contract.Result<IRVarName>() != null);

			if(!this.m_varInternTable.ContainsKey(name))
				this.m_varInternTable.Add(name, new IRVarName(name));
			return this.m_varInternTable[name];
		}

		/// <summary>
		/// Lookup a given var name, which MUST already exist.
		/// </summary>
		[Pure]
		public IRVarName LookupLocalVarName(string name)
		{
			Contract.Requires(!String.IsNullOrEmpty(name));
			Contract.Ensures(Contract.Result<IRVarName>() != null);

			return this.m_varInternTable[name]; 
		}

		/// <summary>
		/// Lookup a given var name, return the name of null if the name is not known.
		/// </summary>
		[Pure]
		public IRVarName LookupLocalVarNameNullIfNotDefined(string name)
		{
			if(!this.m_varInternTable.ContainsKey(name))
				return null;

			return this.m_varInternTable[name];
		}

		////////////////////////////////////////////
		//For static fields

		/// <summary>
		/// Intern a new static field with the given info.
		/// </summary>
		internal IRStaticField InternNewStaticField(IRType defClass, string name, IRType ftype)
		{
			Contract.Requires(defClass != null);
			Contract.Requires(!String.IsNullOrEmpty(name));
			Contract.Requires(ftype != null);
			Contract.Ensures(Contract.Result<IRStaticField>() != null);

			Tuple<IRType, string> lookupPair = new Tuple<IRType, string>(defClass, name);
			if(!this.m_staticInternTable.ContainsKey(lookupPair))
				this.m_staticInternTable.Add(new Tuple<IRType, string>(defClass, name), new IRStaticField(defClass, name, ftype));
			return this.m_staticInternTable[lookupPair];
		}

		/// <summary>
		/// Lookup a given static field, which MUST already exist.
		/// </summary>
		[Pure]
		public IRStaticField LookupStaticField(IRType defClass, string name)
		{
			Contract.Requires(defClass != null);
			Contract.Requires(!String.IsNullOrEmpty(name));
			Contract.Ensures(Contract.Result<IRStaticField>() != null);

			Tuple<IRType, String> lookupPair = new Tuple<IRType, string>(defClass, name);
			return this.m_staticInternTable[lookupPair];
		}

		/// <summary>
		/// Get all the static fields that appear in any type in this program.
		/// THIS IS SLOW AND COPIES -- DO NOT USE IN PERFORMANCE SENSITIVE LOCATIONS.
		/// </summary>
		public void FillVectorWithAllStaticFields(List<IRStaticField> sfv)
		{
			Contract.Requires(sfv != null);

			foreach(IRStaticField sf in this.m_staticInternTable.Values)
				sfv.Add(sf);
		}
	}
}
